package com.heima.admin.service.impl;
import java.util.Date;

import com.alibaba.fastjson.JSONArray;
import com.heima.admin.feign.ArticleFeign;
import com.heima.admin.feign.WemediaFeign;
import com.heima.admin.mapper.AdChannelMapper;
import com.heima.admin.mapper.AdSensitiveMapper;
import com.heima.admin.service.WemediaNewsAutoScanService;
import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.constants.admin.AuthConstants;
import com.heima.common.fastdfs.FastDFSClient;
import com.heima.common.message.ArticleForEsConstants;
import com.heima.model.admin.dtos.NewsAuthDto;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.pojos.ApAuthor;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.media.pojos.WmNews;
import com.heima.model.media.pojos.WmUser;
import com.heima.model.media.vo.WmNewsVo;
import com.heima.utils.common.SensitiveWordUtil;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;

/**
 * @作者 itcast
 * @创建日期 2021/1/31 11:16
 **/
@Service
@Slf4j
public class WemediaNewsAutoScanServiceImpl implements WemediaNewsAutoScanService {
    @Autowired
    WemediaFeign wemediaFeign;
    @Value("${fdfs.url}")
    String fileServerUrl;
    @Autowired
    KafkaTemplate<String,String> kafkaTemplate;



    @GlobalTransactional
    @Override
    public void autoScanByMediaNewsId(Integer id) {
        // 1. 检查ID是否正确
        if(id == null){
            log.error("自动审核的文章id为空");
            return;
        }
        // 2. 使用Feign获取wmNews的信息
        WmNews wmNews = wemediaFeign.findById(id);
        if(wmNews == null){
            log.error("自动审核的文章不存在 id==>{}",id);
            return;
        }
        // 3. 如果文章状态为 8 或 4, 并且发布时间如果小于等于当前时间
        long publishTime = wmNews.getPublishTime().getTime();
        if((wmNews.getStatus()==4||(wmNews.getStatus()==8))&& publishTime<=System.currentTimeMillis()
        ){
            saveAppArticle(wmNews);
            return;
        }
        // 4. 如果文章状态为 1 ,待审核文章，执行审核步骤
        if(wmNews.getStatus()==1){
            // 4.1   抽取出文章所包含的文本内容和图片内容
            //  返回map格式:   key: text  value: 内容字符串     key: images    value: 图片url集合
            Map<String,Object> textAndImageMap = handleTextAndImages(wmNews);

            // 4.2   阿里云文本反垃圾审核
            String text = (String) textAndImageMap.get("text");
            if(StringUtils.isNotBlank(text)){
                //         ==> 如果失败 直接将文章状态 修改为2  如果结果时review 修改为3待人工审核
                boolean textResult = handleTextScan(wmNews,text); // pass blocked review
                if(!textResult) return;
            }
            // 4.3   阿里云图片审核
            List<String> images = (List<String>)textAndImageMap.get("images");
            if(images!=null && !images.isEmpty()){
                //         ==> 如果失败 直接将文章状态 修改为2 审核失败  如果结果时review 修改为3待人工审核
                boolean imageResult = handleImagesScan(wmNews,images);
                if(!imageResult) return;
            }
            // 4.4   查询敏感词库，进行敏感词审核，
            if(StringUtils.isNotBlank(text)){
                //         ==> 如果查询内容中包含敏感词 修改为2 审核失败
                boolean sensitiveResult = handleSensitive(wmNews,text); // pass blocked review
                if(!sensitiveResult) return;
            }
            // 4.5   如果审核通过
            if(publishTime <= System.currentTimeMillis()){
                //  ==> 发布时间 小于等于时间   发布该文章
                saveAppArticle(wmNews);
            }else {
                // ==>  将文章状态改为 8 代表自动审核通过 修改状态
                updateWmNews(wmNews,(short)8,"审核通过");
            }
            return;
        }
    }

    @Override
    public ResponseResult findNews(NewsAuthDto dto) {
        //1.检查参数
        if(dto == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.远程调用自媒体服务获取文章列表
        PageResponseResult list = wemediaFeign.findList(dto);
        list.setHost(fileServerUrl);
        return list;
    }

    @Override
    public ResponseResult findOne(Integer id) {
        if(id == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        WmNewsVo wmNewsVo = wemediaFeign.findWmNewsVo(id);
        ResponseResult responseResult = ResponseResult.okResult(wmNewsVo);
        responseResult.setHost(fileServerUrl);
        return responseResult;
    }
    @Override
    public ResponseResult updateByStatus(NewsAuthDto dto, Integer type) {
        // 1. 检查参数
        if(dto == null || dto.getId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        // 2. 根据id查询wmNews的数据
        WmNews wmNews = wemediaFeign.findById(dto.getId());
        if(wmNews == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
        }
        // 0驳回  1通过
        if(type == 0){
            updateWmNews(wmNews,(short)2,dto.getTitle());
        }else if (type == 1){
            updateWmNews(wmNews,(short)4,"人工审核通过");
        }
        return ResponseResult.okResult();
    }

    /**
     * 当前方法为发布方法
     * @param wmNews
     */
    private void saveAppArticle(WmNews wmNews) {
        // 1. 保存ap_article表信息
        ApArticle apArticle = saveArticle(wmNews);
        // 2. 保存ap_article_config配置表信息
        saveArticleConfig(apArticle);
        // 3. 保存ap_article_content文章内容表信息
        saveArticleContent(apArticle,wmNews);
        // 4. 将文章状态改为 9
        wmNews.setArticleId(apArticle.getId());// 关联app端文章id
        updateWmNews(wmNews,(short)9,"文章发布");
        // 审核通过 发布文章，  需要通知搜索微服务 添加该文章索引
        kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_ADD,String.valueOf(apArticle.getId()));
    }

    /**
     * 保存文章内容
     * @param apArticle
     * @param wmNews
     */
    private void saveArticleContent(ApArticle apArticle, WmNews wmNews) {
        ApArticleContent apArticleContent = new ApArticleContent();
        apArticleContent.setArticleId(apArticle.getId());
        apArticleContent.setContent(wmNews.getContent());
        articleFeign.saveArticleContent(apArticleContent);
    }

    /**
     * 保存文章配置信息
     * @param apArticle
     */
    private void saveArticleConfig(ApArticle apArticle) {
        ApArticleConfig apArticleConfig = new ApArticleConfig();
        apArticleConfig.setArticleId(apArticle.getId());
        apArticleConfig.setIsComment(true);
        apArticleConfig.setIsForward(true);
        apArticleConfig.setIsDown(false);
        apArticleConfig.setIsDelete(false);
        articleFeign.saveArticleConfig(apArticleConfig);
    }

    @Autowired
    ArticleFeign articleFeign;
    @Autowired
    AdChannelMapper adChannelMapper;

    /**
     * app端文章添加
     * @param wmNews
     * @return
     */
    private ApArticle saveArticle(WmNews wmNews) {
        ApArticle apArticle = new ApArticle();
        apArticle.setTitle(wmNews.getTitle());
        apArticle.setLayout(wmNews.getType()); // 单图 多图 无图
        apArticle.setFlag((byte)0);
        apArticle.setImages(wmNews.getImages()); // 封面图片
        apArticle.setLabels(wmNews.getLabels());
        apArticle.setCreatedTime(new Date());
        apArticle.setPublishTime(wmNews.getPublishTime());
        Integer userId = wmNews.getUserId(); // WmUser的id   apUserId
        // 获取自媒体用户
        WmUser wmUser = wemediaFeign.findWmUserById(Long.valueOf(userId));
        // 设置作者信息
        ApAuthor author = articleFeign.findAuthorByUserId(String.valueOf(wmUser.getApUserId()));
        if(author !=null){
            apArticle.setAuthorId(Long.valueOf(author.getId()));
            apArticle.setAuthorName(author.getName());
        }
        // 设置频道信息
        AdChannel adChannel = adChannelMapper.selectById(wmNews.getChannelId());
        if(adChannel!=null){
            apArticle.setChannelId(adChannel.getId());
            apArticle.setChannelName(adChannel.getName());
        }
        return articleFeign.saveArticle(apArticle);
    }

    @Autowired
    AdSensitiveMapper adSensitiveMapper;

    /**
     * 处理自定义敏感词
     * @param wmNews
     * @param text
     * @return
     */
    private boolean handleSensitive(WmNews wmNews, String text) {
        // 1. 查询出数据库中维护的敏感词列表
        List<String> allSensitive = adSensitiveMapper.findAllSensitive();
        // 2. 将敏感词列表封装成DFA需要的数据模型( map)
        // TODO 基于redis做一个缓存  先查缓存中是否存在敏感词信息 如果不存在 在查询数据库
        // TODO 敏感词  修改   删除    插入 ==> 请求对应缓存
        SensitiveWordUtil.initMap(allSensitive);
        log.info("敏感词列表加载完成 ==> {}",SensitiveWordUtil.dictionaryMap);
        // 3. 使用DFA进行敏感词过滤，在过滤结果中查看是否有敏感词
        Map<String, Integer> sensitiveResult = SensitiveWordUtil.matchWords(text);
        if(sensitiveResult!=null && sensitiveResult.size()>0){
            updateWmNews(wmNews,(short) 2,"文本中包含敏感词，审核失败");
            log.info("发现敏感词 ==> {}",sensitiveResult);
            return false;
        }
        return true;
    }

    @Autowired
    GreenImageScan greenImageScan;

    @Autowired
    FastDFSClient fastDFSClient;

    /**
     * 阿里云图片审核
     * @param wmNews
     * @param images
     * @return
     */
    private boolean handleImagesScan(WmNews wmNews, List<String> images) {
        // 传入的图片集合为路径集合需要转化成 byte[] 集合
        List<byte[]> imageByteList = new ArrayList<>();
        for (String url : images) {
            //  http://192.168.200.130/group1/M00/00/00/wKjIgmAP3ZuAKo7CAAGCJEqK0S8471.jpg
            // 1. 干掉前缀 group1/M00/00/00/wKjIgmAP3ZuAKo7CAAGCJEqK0S8471.jpg
            String imgUrl = url.replace(fileServerUrl, "").trim();
            // 2. 获取 "/" 的下标
            int index = imgUrl.indexOf("/");
            // 3. 截取分组名 和 文件名
            // group1
            String groupName = imgUrl.substring(0,index);
            // M00/00/00/wKjIgmAP3ZuAKo7CAAGCJEqK0S8471.jpg
            String path = imgUrl.substring(index+1);
            try {
                imageByteList.add(fastDFSClient.download(groupName, path));
            } catch (IOException e) {
                e.printStackTrace();
                log.error("fastdfs下载图片失败==> group={} path={}",groupName,path);
            }
        }
        try {
            Map map = greenImageScan.imageScan(imageByteList);
            String suggest = (String)map.get(AuthConstants.AUTH_SUGGESTION);
            if(AuthConstants.AUTH_PASS.equals(suggest)){
                // 审核通过
                return true;
            }else if(AuthConstants.AUTH_REVIEW.equals(suggest)){
                updateWmNews(wmNews,(short) 3,"图片中有不确定因素，需要进行人工审核");
            }else {
                updateWmNews(wmNews,(short) 2,"图片中有违规，图片审核失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("调用阿里云图片检测服务失败==>{}",e.getMessage());
        }
        return false;
    }

//    public static void main(String[] args) {
//        String str = "group1/M00/00/00/wKjIgmAP3ZuAKo7CAAGCJEqK0S8471.jpg";
//        int index = str.indexOf("/");
//        String groupName = str.substring(0,index);
//        String path = str.substring(index+1);
//        System.out.println(groupName);
//        System.out.println(path);
//    }


    @Autowired
    GreenTextScan greenTextScan; // 文本扫描

    /**
     * 阿里云文本审核
     * @param wmNews
     * @param text
     * @return
     */
    private boolean handleTextScan(WmNews wmNews, String text) {
        try {
            Map map = greenTextScan.greeTextScan(text);
            // 获取审核建议字段
            String suggest = (String)map.get(AuthConstants.AUTH_SUGGESTION);
            if(AuthConstants.AUTH_PASS.equals(suggest)){
                // 审核通过
                return true;
            }else if(AuthConstants.AUTH_REVIEW.equals(suggest)){
                // 需要人工审核修改状态
                updateWmNews(wmNews,(short)3,"文章内容中有不确定因素，需要人工审核");
            }else {
                // 修改为审核失败的状态
                updateWmNews(wmNews,(short)2,"文章内容中有违规信，审核失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("阿里云文本审核出现异常, 当前审核新闻ID==>{}  异常原因==>{}",wmNews.getId(),e.getMessage());
        }
        return false;
    }

    /**
     * 修改自媒体新闻
     * @param wmNews  新闻对象
     * @param status  状态
     * @param msg     原因
     */
    private void updateWmNews(WmNews wmNews,Short status,String msg){
        wmNews.setStatus(status);
        wmNews.setReason(msg);
        wemediaFeign.updateWmNews(wmNews);
    }
    /**
     * 抽取文本内容 及 图片内容 放入map集合
     * @param wmNews key: text  value: 内容字符串     key: images    value: 图片url信息 集合
     *
     *
     *       [{"type":"text","value":"非常有瓜吃"},{"type":"image","value":"http://192.168.200.130/group1/M00/00/00/wKjIgmAP3USANMJlAAaGbuai9k0493.png"},{"type":"image","value":"http://192.168.200.130/group1/M00/00/00/wKjIgmAP3ZuAKo7CAAGCJEqK0S8471.jpg"},{"type":"text","value":"请在这里输入正文"}]
     * @return
     */
    private Map<String, Object> handleTextAndImages(WmNews wmNews) {
        // 用于存储文本内容
        StringBuilder contentStr = new StringBuilder(wmNews.getTitle());
        // 用于存储图片路径集合
        List<String> images = new ArrayList<>();


        List<Map> mapList = JSONArray.parseArray(wmNews.getContent(),Map.class);


        // 遍历map集合
        for (Map map : mapList) {
            // 拼接文本内容
            String type = (String)map.get("type");
            if("text".equals(type)){
                contentStr.append(map.get("value"));
            }
            // 添加图片
            if("image".equals(type)){
                images.add((String)map.get("value"));
            }
        }
        // 添加封面图片
        String coverImages = wmNews.getImages();
        if(StringUtils.isNotBlank(coverImages) && wmNews.getType() != 0){
            // 因为封面字段使用 逗号拼接的图片列表 需要split切割 得到图片数组
            images.addAll(Arrays.asList(coverImages.split(",")));
        }
        Map<String,Object> map = new HashMap<>();
        map.put("text",contentStr.toString());
        map.put("images",images);
        return map;
    }
}
